package cz.proks.schoolpupilbook.gwt.widget.common;

import java.util.Date;
import java.util.LinkedList;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.AsyncDataProvider;
import com.google.gwt.view.client.HasData;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.gwt.view.client.TreeViewModel;

import cz.proks.schoolpupilbook.gwt.core.ResultNotifiable;
import cz.proks.schoolpupilbook.gwt.core.Test;
import cz.proks.schoolpupilbook.gwt.service.ListService;
import cz.proks.schoolpupilbook.gwt.service.MarkService;
import cz.proks.schoolpupilbook.gwt.service.ServiceFactory;
import cz.proks.schoolpupilbook.shared.domain.Course;
import cz.proks.schoolpupilbook.shared.domain.Mark;
import cz.proks.schoolpupilbook.shared.domain.User;

public class StudentView extends Composite {	
	interface CoursesUiBinder extends UiBinder<Widget, StudentView> {
	}
	
	private static CoursesUiBinder uiBinder = GWT.create(CoursesUiBinder.class);	
	
	@UiField (provided = true) PupilCellBrowser browser;
	private CourseTreeModel model;
	private User user;

	public StudentView(User user) {
		PupilCellBrowser.Resources resources = GWT.create(PupilCellBrowser.Resources.class);
		
		this.model = new CourseTreeModel(user);
		browser = new PupilCellBrowser(model, null, resources);
		browser.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
		browser.setAnimationEnabled(true);
		browser.setWidth("100%");		
		browser.setDefaultColumnWidth(260);
		browser.setMinimumColumnWidth(120);
		
		
		initWidget(uiBinder.createAndBindUi(this));		
	}		
	
	public static class CourseTreeModel implements TreeViewModel {
		private final SingleSelectionModel<Course> courseSelection;
		private final SingleSelectionModel<SimpleMarkUI> markSelection;
		private final User user;
		
		public CourseTreeModel(User user) {
			courseSelection = new SingleSelectionModel<Course>();
			markSelection = new SingleSelectionModel<SimpleMarkUI>();
			this.user = user;
		}
						
		@Override
		public <T> NodeInfo<?> getNodeInfo(T value) {
			// Start point - list courses
			if (value == null) { 	
				AsyncDataProvider<Course> dataProvider = new CourseDataProvider(user);
				Cell<Course> cell = new CourseCell();													
				return new DefaultNodeInfo<Course>(dataProvider, cell, courseSelection, null);
			}
			
			else if (value instanceof Course) {
				AsyncDataProvider<SimpleMarkUI> dataProvider = new SimpleMarkDataProvider(user, getSelectedCourse());
				Cell<SimpleMarkUI> cell = new SimpleMarkCell();				
				return new DefaultNodeInfo<SimpleMarkUI>(dataProvider, cell, markSelection, null);
			}
			
			else if (value instanceof SimpleMarkUI) {
				AsyncDataProvider<Mark> dataProvider = new MarkDataProvider(((SimpleMarkUI) value).getMark().getId());
				Cell<Mark> cell = new MarkCell();						
				return new DefaultNodeInfo<Mark>(dataProvider, cell);
			}
					
			return null;
		}

		@Override
		public boolean isLeaf(Object value) {
			return value instanceof Mark;
		}		
		
		public Course getSelectedCourse() {
			return courseSelection.getSelectedObject();
		}
		
		public Mark getSelectedMark() {
			return markSelection.getSelectedObject().getMark();
		}
		
		private LinkedList<SimpleMarkUI> wrapMarkList(LinkedList<Mark> markList) {
			LinkedList<SimpleMarkUI> simpleMarkUIs = new LinkedList<StudentView.SimpleMarkUI>();
			
			if (markList == null)
				return simpleMarkUIs;
			
			for (Mark mark : markList) {
				simpleMarkUIs.add(new SimpleMarkUI(mark));
			}
			
			return simpleMarkUIs;
		}
	}	
		
	private static class SimpleMarkUI implements Comparable<SimpleMarkUI>{
		private final DateTimeFormat timeFormater = DateTimeFormat.getFormat("dd.MM.yyyy HH:mm");
		private Mark mark;

		public SimpleMarkUI(Mark mark) {
			super();
			this.mark = mark;
		}
		
		public Mark getMark() {
			return mark;
		}
		
		public Date getTime() {
			return mark.getTime();
		}
		
		public String getFormatedTime() {
			return timeFormater.format(getTime());
		}
		
		@Override
		public int compareTo(SimpleMarkUI o) {
			return o == null ? 1 : getMark().compareTo(o.getMark());
		}
	}
	
	private static class CourseCell extends AbstractCell<Course> {
		@Override
		public void render(Context context, Course value, SafeHtmlBuilder sb) {
			if (value != null)
				sb.appendEscaped(value.getName());			
		}		
	}
	
	private static class SimpleMarkCell extends AbstractCell<SimpleMarkUI> {
		@Override
		public void render(Context context, SimpleMarkUI value, SafeHtmlBuilder sb) {
			if (value != null)
				sb.appendEscaped(value.getFormatedTime());			
		}		
	}
	
	private static class MarkCell extends AbstractCell<Mark> {
		@Override
		public void render(Context context, Mark value, SafeHtmlBuilder sb) {
			if (value != null) {
				int commentSize = value.getComments() != null ? value.getComments().size() : 0; 
				sb.appendHtmlConstant("<table><tr><td>Lector: <b>");
				sb.appendEscaped(value.getLector().getFullName());
				sb.appendHtmlConstant("</b></td></tr><tr><td>Mark: <b>");
				sb.appendEscaped(value.getMark());
				sb.appendHtmlConstant("</b></td></tr><tr><td>Comments: <b>");		
				sb.append(commentSize);
				sb.appendHtmlConstant("</b></td></tr></table>");
			}			
		}		
	}
	
	private static class CourseDataProvider extends AsyncDataProvider<Course> {
		private User user;
				
		public CourseDataProvider(User user) {
			super();
			this.user = user;
		}

		@Override
		protected void onRangeChanged(HasData<Course> display) {
			getData();
		}
		
		private void getData() {
			ListService listService = ServiceFactory.getListService();
			listService.listCourses(user, new ResultNotifiable<LinkedList<Course>>() {
				
				@Override
				public void onSucces(LinkedList<Course> object) {
					updateRowData(0, object);
					updateRowCount(object.size(), true);
				}
				
				@Override
				public void onError(Throwable t) {
					Window.alert("Communication failed: " + t.getLocalizedMessage());
				}
			});			
		}		
	}
	
	private static class SimpleMarkDataProvider extends AsyncDataProvider<SimpleMarkUI> {
		private User user;
		private Course course;
				
		public SimpleMarkDataProvider(User user, Course course) {
			super();
			this.user = user;
			this.course = course;
		}

		@Override
		protected void onRangeChanged(HasData<SimpleMarkUI> display) {
			getData();
		}
		
		private void getData() {
			ListService listService = ServiceFactory.getListService();
			listService.listMarks(user, course, new ResultNotifiable<LinkedList<Mark>>() {

				@Override
				public void onSucces(LinkedList<Mark> object) {
					LinkedList<SimpleMarkUI> simpleMarkList = new LinkedList<StudentView.SimpleMarkUI>();
					
					for (Mark mark : object) {
						simpleMarkList.add(new SimpleMarkUI(mark));
					}
					
					updateRowData(0, simpleMarkList);
					updateRowCount(simpleMarkList.size(), true);
				}

				@Override
				public void onError(Throwable t) {
					Window.alert("Communication failed: " + t.getLocalizedMessage());
				}				
			});			
		}		
	}
	
	private static class MarkDataProvider extends AsyncDataProvider<Mark> {
		private String markId;
		
		public MarkDataProvider(String markId) {
			super();
			this.markId = markId;
		}

		@Override
		protected void onRangeChanged(HasData<Mark> display) {
			getData();
		}
		
		private void getData() {
			MarkService markService = ServiceFactory.getMarkService();
			markService.get(markId, new ResultNotifiable<Mark>() {

				@Override
				public void onSucces(Mark object) {
					LinkedList<Mark> markList = new LinkedList<Mark>();
					markList.add(object);
					
					updateRowData(0, markList);
					updateRowCount(markList.size(), true);
				}

				@Override
				public void onError(Throwable t) {
					Window.alert("Communication failed: " + t.getLocalizedMessage());
				}
				
			});									
		}		
	}
}
